package com.itext5.xfdfReader;

/*
*
* This file is part of the iText (R) project.
* Copyright (c) 1998-2016 iText Group NV
* Authors: Bruno Lowagie, Paulo Soares, et al.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License version 3
* as published by the Free Software Foundation with the addition of the
* following permission added to Section 15 as permitted in Section 7(a):
* FOR ANY PART OF THE COVERED WORK IN WHICH THE COPYRIGHT IS OWNED BY
* ITEXT GROUP. ITEXT GROUP DISCLAIMS THE WARRANTY OF NON INFRINGEMENT
* OF THIRD PARTY RIGHTS
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE.
* See the GNU Affero General Public License for more details.
* You should have received a copy of the GNU Affero General Public License
* along with this program; if not, see http://www.gnu.org/licenses or write to
* the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA, 02110-1301 USA, or download the license from the following URL:
* http://itextpdf.com/terms-of-use/
*
* The interactive user interfaces in modified source and object code versions
* of this program must display Appropriate Legal Notices, as required under
* Section 5 of the GNU Affero General Public License.
*
* In accordance with Section 7(b) of the GNU Affero General Public License,
* a covered work must retain the producer line in every PDF that is created
* or manipulated using iText.
*
* You can be released from the requirements of the license by purchasing
* a commercial license. Buying such a license is mandatory as soon as you
* develop commercial activities involving the iText software without
* disclosing the source code of your own applications.
* These activities include: offering paid services to customers as an ASP,
* serving PDFs on the fly in a web application, shipping iText with a closed
* source product.
*
* For more information, please contact iText Software Corp. at this
* address: sales@itextpdf.com
*/

import java.io.ByteArrayInputStream;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Stack;

import com.itext5.xfdfReader.MessageLocalization;
import com.itext5.xfdfReader.SimpleXMLDocHandler;
import com.itext5.xfdfReader.SimpleXMLParser;

/**
* Reads a XFDF.
* @author Leonard Rosenthol (leonardr@pdfsages.com)
*/
public class XfdfReader implements SimpleXMLDocHandler {
	// stuff used during parsing to handle state
	
	private boolean foundRoot = false;
   private final Stack<String> fieldNames = new Stack<String>();
   private final Stack<String> fieldValues = new Stack<String>();

   // storage for the field list and their values
	HashMap<String, String>	fields;
	/**
	 * Storage for field values if there's more than one value for a field.
	 * @since	2.1.4
	 */
	protected HashMap<String, List<String>> listFields;

	// storage for the path to referenced PDF, if any
	String	fileSpec;

  /**
   * Reads an XFDF form.
    * @param filename the file name of the form
    * @throws IOException on error
    */
   public XfdfReader(String filename) throws IOException {
       FileInputStream fin = null;
       try {
           fin = new FileInputStream(filename);
           SimpleXMLParser.parse(this, fin);
       }
       finally {
           try{if (fin != null) {fin.close();}}catch(Exception e){}
       }
   }

   /**
    * Reads an XFDF form.
    * @param xfdfIn the byte array with the form
    * @throws IOException on error
    */
   public XfdfReader(byte xfdfIn[]) throws IOException {
       this(new ByteArrayInputStream(xfdfIn));
  }

   /**
    * Reads an XFDF form.
    * @param is an InputStream to read the form
    * @throws IOException on error
    * @since 5.0.1
    */
   public XfdfReader(InputStream is) throws IOException {
       SimpleXMLParser.parse( this, is);
  }

   /** Gets all the fields. The map is keyed by the fully qualified
    * field name and the value is a merged <CODE>PdfDictionary</CODE>
    * with the field content.
    * @return all the fields
    */
   public HashMap<String, String> getFields() {
       return fields;
   }

   /** Gets the field value.
    * @param name the fully qualified field name
    * @return the field's value
    */
   public String getField(String name) {
       return fields.get(name);
   }

   /** Gets the field value or <CODE>null</CODE> if the field does not
    * exist or has no value defined.
    * @param name the fully qualified field name
    * @return the field value or <CODE>null</CODE>
    */
   public String getFieldValue(String name) {
       String field = fields.get(name);
       if (field == null)
           return null;
       else
       	return field;
   }

   /**
    * Gets the field values for a list or <CODE>null</CODE> if the field does not
    * exist or has no value defined.
    * @param name the fully qualified field name
    * @return the field values or <CODE>null</CODE>
    * @since	2.1.4
    */
   public List<String> getListValues(String name) {
       return listFields.get(name);
   }

   /** Gets the PDF file specification contained in the FDF.
    * @return the PDF file specification contained in the FDF
    */
   public String getFileSpec() {
       return fileSpec;
   }

   /**
    * Called when a start tag is found.
    * @param tag the tag name
    * @param h the tag's attributes
    */
   public void startElement(String tag, Map<String, String> h)
   {
       if ( !foundRoot ) {
           if (!tag.equals("xfdf"))
               throw new RuntimeException(MessageLocalization.getComposedMessage("root.element.is.not.xfdf.1", tag));
           else
           	foundRoot = true;
       }

       if ( tag.equals("xfdf") ){

   	} else if ( tag.equals("f") ) {
   		fileSpec = h.get( "href" );
   	} else if ( tag.equals("fields") ) {
           fields = new HashMap<String, String>();		// init it!
           listFields = new HashMap<String, List<String>>();
   	} else if ( tag.equals("field") ) {
   		String	fName = h.get( "name" );
   		fieldNames.push( fName );
   	} else if ( tag.equals("value") ) {
   		fieldValues.push( "" );
   	}
   }
   /**
    * Called when an end tag is found.
    * @param tag the tag name
    */
   public void endElement(String tag) {
       if ( tag.equals("value") ) {
           String	fName = "";
           for (int k = 0; k < fieldNames.size(); ++k) {
               fName += "." + fieldNames.elementAt(k);
           }
           if (fName.startsWith("."))
               fName = fName.substring(1);
           String fVal = fieldValues.pop();
           String old = fields.put( fName, fVal );
           if (old != null) {
           	List<String> l = listFields.get(fName);
           	if (l == null) {
           		l = new ArrayList<String>();
           		l.add(old);
           	}
           	l.add(fVal);
           	listFields.put(fName, l);
           }
       }
       else if (tag.equals("field") ) {
           if (!fieldNames.isEmpty())
               fieldNames.pop();
       }
   }

   /**
    * Called when the document starts to be parsed.
    */
   public void startDocument()
   {
       fileSpec = "";
   }
   /**
    * Called after the document is parsed.
    */
   public void endDocument()
	{

	}
   /**
    * Called when a text element is found.
    * @param str the text element, probably a fragment.
    */
   public void text(String str)
   {
       if (fieldNames.isEmpty() || fieldValues.isEmpty())
           return;

       String val = fieldValues.pop();
       val += str;
       fieldValues.push(val);
   }
}
